﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Reflection;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Design;

namespace SBS_Map_Editor
{
    public class Settings
    {
        #region Constructor
        protected Settings() { }
        #endregion

        #region Helper Functions
        public void RestoreDefaults()
        {
            // Get top level type of the object
            Type t = this.GetType();

            // Get all of the object's private fields
            FieldInfo[] fields = t.GetFields(BindingFlags.NonPublic
                | BindingFlags.Public | BindingFlags.Instance);

            // Iterate over all fields and look for DefaultValue attributes
            foreach (FieldInfo fi in fields)
            {
                // find all default attributes
                object[] attrlist = fi.GetCustomAttributes(typeof(DefaultValueAttribute), true);

                // no attribute found, continue
                if (attrlist.Length == 0)
                    continue;

                // get the appropriate attribute type and set its value on the field
                DefaultValueAttribute def = (DefaultValueAttribute)attrlist[0];
                object value = def.Value;

                if (value != null)
                {
                    attrlist = fi.GetCustomAttributes(typeof(TypeConverterAttribute), true);
                    if (attrlist.Length > 0)
                    {
                        TypeConverterAttribute cattr = (TypeConverterAttribute)attrlist[0];
                        Type ctype = Type.GetType(cattr.ConverterTypeName);

                        if (ctype != null)
                        {
                            TypeConverter conv = (TypeConverter)Activator.CreateInstance(ctype);
                            if ((conv != null) && conv.CanConvertFrom(value.GetType()))
                                value = conv.ConvertFrom(value);
                        }
                    }
                }

                fi.SetValue(this, value);
            }
        }
        #endregion
    }

    public class ManipulatorSettings : Settings
    {
        #region TranslationSettings Class
        public class TranslationSettings : Settings
        {
            #region Variables
            [DefaultValue(0.05f)]
            private float mAxisRadius;

            [DefaultValue(1.5f)]
            private float mAxisExtent;

            [DefaultValue(0.1f)]
            private float mConeRadius;

            [DefaultValue(0.5f)]
            private float mConeHeight;

            [DefaultValue(0.75f)]
            private float mPlaneQuadrantSize;

            [DefaultValue(AxisDirections.Positive)]
            private AxisDirections mAxisDrawMode;
            #endregion

            #region Properties
            public float AxisRadius
            {
                get { return mAxisRadius; }
                set
                {
                    mAxisRadius = MathHelper.Clamp(value, 0, float.MaxValue);
                }
            }

            public float AxisExtent
            {
                get { return mAxisExtent; }
                set
                {
                    mAxisExtent = MathHelper.Clamp(value, 0, float.MaxValue);
                }
            }

            public float ConeRadius
            {
                get { return mConeRadius; }
                set
                {
                    mConeRadius = MathHelper.Clamp(value, 0, float.MaxValue);
                }
            }

            public float ConeHeight
            {
                get { return mConeHeight; }
                set
                {
                    mConeHeight = MathHelper.Clamp(value, 0, float.MaxValue);
                }
            }

            public float PlaneQuadrantSize
            {
                get { return mPlaneQuadrantSize; }
                set
                {
                    mPlaneQuadrantSize = MathHelper.Clamp(value, 0, float.MaxValue);
                }
            }

            public AxisDirections AxisDrawMode
            {
                get { return mAxisDrawMode; }
                set { mAxisDrawMode = value; }
            }
            #endregion

            #region Constructor
            internal TranslationSettings() { }
            #endregion
        }
        #endregion

        #region RotationSettings Class
        public class RotationSettings : Settings
        {
            #region Variables
            [DefaultValue(1.25f)]
            private float mInnerRadius;

            [DefaultValue(1.50f)]
            private float mOuterRadius;
            #endregion

            #region Properties
            public float InnerRadius
            {
                get { return mInnerRadius; }
                set
                {
                    mInnerRadius = MathHelper.Clamp(value, 0, float.MaxValue);

                    if (mOuterRadius < mInnerRadius)
                        mOuterRadius = mInnerRadius;
                }
            }

            public float OuterRadius
            {
                get { return mOuterRadius; }
                set
                {
                    mOuterRadius = MathHelper.Clamp(value, 0, float.MaxValue);

                    if (mInnerRadius > mOuterRadius)
                        mInnerRadius = mOuterRadius;
                }
            }
            #endregion

            #region Constructor
            internal RotationSettings() { }
            #endregion
        }
        #endregion

        #region ScaleSettings Class
        public class ScaleSettings : Settings
        {
            #region Variables
            [DefaultValue(0.05f)]
            private float mAxisRadius;

            [DefaultValue(1.0f)]
            private float mAxisExtent;

            [DefaultValue(0.1f)]
            private float mAxisHandleSize;

            [DefaultValue(0.1f)]
            private float mPlaneHandleSize;

            [DefaultValue(0.3f)]
            private float mUniformHandleRadius;

            [DefaultValue(AxisDirections.Positive)]
            private AxisDirections mAxisDrawMode;
            #endregion

            #region Properties
            public float AxisRadius
            {
                get { return mAxisRadius; }
                set
                {
                    mAxisRadius = MathHelper.Clamp(value, 0, float.MaxValue);
                }
            }

            public float AxisExtent
            {
                get { return mAxisExtent; }
                set
                {
                    mAxisExtent = MathHelper.Clamp(value, 0, float.MaxValue);
                }
            }

            public float AxisHandleSize
            {
                get { return mAxisHandleSize; }
                set
                {
                    mAxisHandleSize = MathHelper.Clamp(value, 0, float.MaxValue);
                }
            }

            public float PlaneHandleRadius
            {
                get { return mPlaneHandleSize; }
                set
                {
                    mPlaneHandleSize = MathHelper.Clamp(value, 0, float.MaxValue);
                }
            }

            public float UniformHandleRadius
            {
                get { return mUniformHandleRadius; }
                set
                {
                    mUniformHandleRadius = MathHelper.Clamp(value, 0, float.MaxValue);
                }
            }

            public AxisDirections AxisDrawMode
            {
                get { return mAxisDrawMode; }
                set { mAxisDrawMode = value; }
            }
            #endregion

            #region Constructor
            internal ScaleSettings() { }
            #endregion
        }
        #endregion

        #region Variables
        private TranslationSettings mTranslationSettings;
        private RotationSettings mRotationSettings;
        private ScaleSettings mScaleSettings;
        #endregion

        #region Color Variables
        [DefaultValue(0xFF0000)]
        [TypeConverter(typeof(ColorConverter))]
        private Color mXAxisColor;

        [DefaultValue(0x00FF00)]
        [TypeConverter(typeof(ColorConverter))]
        private Color mYAxisColor;

        [DefaultValue(0x0000FF)]
        [TypeConverter(typeof(ColorConverter))]
        private Color mZAxisColor;

        [DefaultValue(0x0000FF)]
        [TypeConverter(typeof(ColorConverter))]
        private Color mXYPlaneColor;

        [DefaultValue(0x00FF00)]
        [TypeConverter(typeof(ColorConverter))]
        private Color mXZPlaneColor;

        [DefaultValue(0xFF0000)]
        [TypeConverter(typeof(ColorConverter))]
        private Color mYZPlaneColor;

        [DefaultValue(0xFFEBCD)]
        [TypeConverter(typeof(ColorConverter))]
        private Color mUniformColor;

        [DefaultValue(0xFFFF00)]
        [TypeConverter(typeof(ColorConverter))]
        private Color mSelectionColor;

        [DefaultValue(50.0f)]
        private float mBaseScale;

        [DefaultValue(true)]
        private bool mDrawBounds;
        #endregion

        #region Properties
        public TranslationSettings Translation
        {
            get { return mTranslationSettings; }
        }

        public RotationSettings Rotation
        {
            get { return mRotationSettings; }
        }

        public ScaleSettings Scale
        {
            get { return mScaleSettings; }
        }

        public Color XAxisColor
        {
            get { return mXAxisColor; }
            set { mXAxisColor = value; }
        }

        public Color YAxisColor
        {
            get { return mYAxisColor; }
            set { mYAxisColor = value; }
        }

        public Color ZAxisColor
        {
            get { return mZAxisColor; }
            set { mZAxisColor = value; }
        }

        public Color XYPlaneColor
        {
            get { return mXYPlaneColor; }
            set { mXYPlaneColor = value; }
        }

        public Color YXPlaneColor
        {
            get { return mXYPlaneColor; }
            set { mXYPlaneColor = value; }
        }

        public Color XZPlaneColor
        {
            get { return mXZPlaneColor; }
            set { mXZPlaneColor = value; }
        }

        public Color ZXPlaneColor
        {
            get { return mXZPlaneColor; }
            set { mXZPlaneColor = value; }
        }

        public Color YZPlaneColor
        {
            get { return mYZPlaneColor; }
            set { mYZPlaneColor = value; }
        }

        public Color ZYPlaneColor
        {
            get { return mYZPlaneColor; }
            set { mYZPlaneColor = value; }
        }

        public Color UniformColor
        {
            get { return mUniformColor; }
            set { mUniformColor = value; }
        }

        public Color SelectionColor
        {
            get { return mSelectionColor; }
            set { mSelectionColor = value; }
        }

        public float BaseScale
        {
            get { return mBaseScale; }
            set { mBaseScale = MathHelper.Clamp(value, float.Epsilon, float.MaxValue); }
        }

        public bool DrawBoundsEnabled
        {
            get { return mDrawBounds; }
            set { mDrawBounds = value; }
        }
        #endregion

        #region Constructor
        internal ManipulatorSettings()
        {
            mTranslationSettings = new TranslationSettings();
            mRotationSettings = new RotationSettings();
            mScaleSettings = new ScaleSettings();

            mTranslationSettings.RestoreDefaults();
            mRotationSettings.RestoreDefaults();
            mScaleSettings.RestoreDefaults();
        }
        #endregion

        #region Helper Fuctions
        public Color GetAxisColor(AxisFlags axis)
        {
            switch (axis)
            {
                case AxisFlags.X:
                    return mXAxisColor;
                case AxisFlags.Y:
                    return mYAxisColor;
                case AxisFlags.Z:
                    return mZAxisColor;
                case AxisFlags.XY:
                    return mXYPlaneColor;
                case AxisFlags.XZ:
                    return mXZPlaneColor;
                case AxisFlags.YZ:
                    return mYZPlaneColor;
                case AxisFlags.XYZ:
                    return mUniformColor;
            }

            return Color.Black;
        }
        #endregion
    }
}